#include <utility>
#include "HttpServer.h"

/**
 * @fn
 * @brief 此函数运行在IO线程，用于回写结果
 * @param[in]
 * @param[out]
 * @retval
 */
void HttpServer::on_work_complete(struct mg_connection *nc, int ev, void *ev_data) 
{
    //work_result *p =(work_result *)ev_data;
    SendHttpRsp(nc, "");
}

/**
 * @fn
 * @brief 构造函数
 * @param[in]
 * @param[out]
 * @retval
 */
HttpServer::HttpServer() 
{
    m_args.isRunning = false;
}

/**
 * @fn
 * @brief
 * @param[in] addr：例如"192.168.1.5:8080" 或 "8080"
 * @param[out]
 * @retval
 */
void HttpServer::Init(const std::string &addr)
{
    m_args.addr = addr;
    mg_mgr_init(&m_args.manager, NULL);
}

/**
 * @fn
 * @brief  启动IO轮询线程及工作线程
 * @param[in]
 * @param[out]
 * @retval
 */
bool HttpServer::Start()
{
    if(!m_args.isRunning)
    {
        struct mg_bind_opts opts = {0};
        opts.user_data = &m_args;

        m_args.isRunning = true;

        mg_connection *connection = mg_bind_opt(&m_args.manager, m_args.addr.c_str(), HttpServer::OnHttpWebsocketEvent, opts);
        if (connection == NULL)
        {
            m_args.isRunning = false;
            return m_args.isRunning;
        }

        /* for both http and websocket */
        mg_set_protocol_http_websocket(connection);

        /* 创建IO轮询线程 */
        if(pthread_create(&m_args.threadID, NULL, IOThread, &m_args) != 0)
        {
            printf("__FILE__, __LINE__, pthread_create err\n");
            m_args.isRunning = false;
            return m_args.isRunning;
        }
    }
    return m_args.isRunning;
}

/**
 * @fn
 * @brief
 * @param[in]
 * @param[out]
 * @retval
 */
void HttpServer::OnHttpWebsocketEvent(mg_connection *connection, int event_type, void *event_data)
{
    /* 区分http和websocket */
    if (event_type == MG_EV_HTTP_REQUEST)
    {
        http_message *http_req = (http_message *)event_data;
        HandleHttpEvent(connection, http_req);
    }
}

/**
 * @fn
 * @brief
 * @param[in]
 * @param[out]
 * @retval
 */
 bool HttpServer::RouteCheck(http_message *http_msg, char *route_prefix)
{
    bool ret = true;
    bool retTemp = true;

    if(mg_vcmp(&http_msg->method, "GET"))
    {
        retTemp = true;
    }
    else if(mg_vcmp(&http_msg->method, "POST"))
    {
        retTemp = true;
    }
    else
    {
        retTemp = false;
    }

    ret = ret && retTemp;

    return ret;
}

/**
 * @fn
 * @brief  注册url和对应的回调函数
 * @param[in] arg:回调函数的参数
 * @param[out]
 * @retval
 */
bool HttpServer::AddHandler(const std::string &url, ReqHandler req_handler, void *arg)
{
    int ret = true;
    m_args.mtx.lock();
    if(m_args.handlerMap.find(url) != m_args.handlerMap.end())
    {
        ret = false;
    }
    else
    {
        HttpServerCallBackBind  callBackBind = {req_handler, arg};
        m_args.handlerMap.insert(std::make_pair(url, callBackBind));
    }
    m_args.mtx.unlock();
    return ret;
}

/**
 * @fn
 * @brief
 * @param[in]
 * @param[out]
 * @retval
 */
void HttpServer::RemoveHandler(const std::string &url)
{
    m_args.mtx.lock();

    auto it = m_args.handlerMap.find(url);
    if (it != m_args.handlerMap.end())
    {
        m_args.handlerMap.erase(it);
    }
    m_args.mtx.unlock();
}

/**
 * @fn
 * @brief
 * @param[in]
 * @param[out]
 * @retval
 */
void HttpServer::SendHttpRsp(mg_connection *connection, std::string result)
{
    /* 未开启CORS,必须先发送header, 暂时还不能用HTTP/2.0 */
    mg_printf(connection, "%s", "HTTP/1.1 200 OK, Content-Type: application/json;charset=UTF-8\r\nTransfer-Encoding: chunked\r\n\r\n");

    /* 以json形式返回 */
    mg_printf_http_chunk(connection, "{ \"result\":\"0\", \"data\":\"%s\"}", result.c_str());
    /*发送空白字符快，结束当前响应 */
    mg_send_http_chunk(connection, "", 0);
}

/**
 * @fn
 * @brief
 * @param[in]
 * @param[out]
 * @retval
 */
void HttpServer::HandleHttpEvent(mg_connection *connection, http_message *http_req)
{
    HttpThreadArgs* pArgs = (HttpThreadArgs*)connection->user_data;

    //std::string req_str = std::string(http_req->message.p, http_req->message.len);
    // printf("got request: %s\n", req_str.c_str());

    /* 先过滤是否已注册的函数回调 */
    std::string url = std::string(http_req->uri.p, http_req->uri.len);
    std::string body = std::string(http_req->body.p, http_req->body.len);

    pArgs->mtx.lock();

    auto it = pArgs->handlerMap.find(url);
    if (it != pArgs->handlerMap.end())
    {
        HttpServerCallBackBind handleBind = it->second;

        bool ret = handleBind.func(url, body, handleBind.arg);
        if(ret)
        {
            SendHttpRsp(connection, "");
        }
        else
        {
            SendHttpRsp(connection, "err in call back");
        }

    }
    else/* 其他请求 */
    {
        mg_printf( connection, "%s","HTTP/1.1 501 Not Implemented\r\nContent-Length: 0\r\n\r\n");
    }

    pArgs->mtx.unlock();
}

/**
 * @fn
 * @brief
 * @param[in]
 * @param[out]
 * @retval
 */
bool HttpServer::Close()
{
    void *thrdRet = NULL;
    if(m_args.isRunning)
    {
        m_args.isRunning = false;
        pthread_join(m_args.threadID, &thrdRet);/* 回收io线程 */
        mg_mgr_free(&m_args.manager);
    }
    return m_args.isRunning;
}

/**
 * @fn
 * @brief 工作线程
 * @param[in]
 * @param[out]
 * @retval
 */
void *HttpServer::WorkerThread(void *param)
{
    HttpThreadArgs *args = (HttpThreadArgs *) param;
    struct work_request req = {0};
    struct work_result res = {0};
    int ret = 0;
    while (args->isRunning) 
    {
        ret = read(args->sockfd[1], &req, sizeof(req));
        if (ret < 0)
        {
            perror("Reading worker sock");
            continue;
        }
        else if(0 == ret)
        {
            /* socket关闭,退出 */
            break;
        }

        std::string url = std::string(req.http_req.uri.p, req.http_req.uri.len);
        std::string body = std::string(req.http_req.body.p, req.http_req.body.len);
        req.func(url, body, req.userArg);
        
        mg_broadcast(&args->manager, on_work_complete, (void *)&res, sizeof(res));
    }

    return NULL;
}

/**
 * @fn
 * @brief IO轮询线程
 * @param[in]
 * @param[out]
 * @retval
 */
void *HttpServer::IOThread(void*arg)
{
    HttpThreadArgs *pArgs = (HttpThreadArgs*)arg;

    while(pArgs->isRunning)
    {
        mg_mgr_poll(&pArgs->manager, 500); //ms
    }

    return NULL;
}

/**
 * @fn
 * @brief
 * @param[in]
 * @param[out]
 * @retval
 */
HttpServer::~HttpServer() 
{
    Close();
}

